<?php


namespace Lib\Contracts;

use Lib\Exceptions\InvalidArgumentException;
use Lib\Exceptions\InvalidResponseException;

/**
 * 拉卡拉支付基础类
 * Class BasicPay
 * @package WeChat\Contracts
 */
abstract class BasicPay
{
    /**
     * 商户配置
     * @var DataArray
     */
    protected $config;

    protected $schema = 'LKLAPI-SHA256withRSA';

    protected $version = '3.0';

    /**
     * 当前请求数据
     * @var DataArray
     */
    protected $params;

    /**
     * 静态缓存
     * @var static
     */
    protected static $cache;


    /**
     * 正常请求网关
     * @var string
     */
    protected $gateway = 'https://s2.lakala.com/api/';


    /**
     * Lakala constructor.
     * @param array $options
     */
    public function __construct(array $options)
    {
        if (empty($options['app_id'])) {
            throw new InvalidArgumentException("Missing Config -- [app_id] AppID 错误");
        }
        if (empty($options['mch_id'])) {
            throw new InvalidArgumentException("Missing Config -- [mch_id] 商户编号 错误");
        }
        if (empty($options['serial_no'])) {
            throw new InvalidArgumentException("Missing Config -- [serial_no] 证书编号 错误");
        }
        if (empty($options['notify_url'])) {
            throw new InvalidArgumentException("Missing Config -- [notify_url] 回调地址 错误");
        }
//        if (empty($options['app_public_key'])) {
//            throw new InvalidArgumentException("Missing Config -- [app_public_path] 接入方唯一编号 错误");
//        }
        if (empty($options['app_private_key'])) {
            throw new InvalidArgumentException("Missing Config -- [app_private_path] 应用私钥 错误");
        }
        if (empty($options['platform_public_key'])) {
            throw new InvalidArgumentException("Missing Config -- [platform_public_path] 平台公钥 错误");
        }

        if (!empty($options['cache_path'])) {
            Tools::$cache_path = $options['cache_path'];
        }
        if (!empty($options['debug'])) {
            $this->gateway = 'https://test.wsmsd.cn/sit/api/';
        }
        $this->config = new DataArray($options);
    }

    /**
     * 静态创建对象
     * @param array $config
     * @return static
     */
    public static function instance(array $config)
    {
        $key = md5(get_called_class() . serialize($config));
        if (isset(self::$cache[$key])) return self::$cache[$key];
        return self::$cache[$key] = new static($config);
    }

    /**
     * 生成支付签名
     * @param array $data 参与签名的数据
     * @param string $signType 参与签名的类型
     * @param string $buff 参与签名字符串前缀
     * @return string
     */
    protected function getSign(string $body)
    {
        [$nonceStr,$timestamp] = [Tools::createNoncestr(12),time()];
        $message = [$this->config->get('app_id'),$this->config->get('serial_no'),$timestamp,$nonceStr,$body,''];
        openssl_sign(implode("\n",$message), $sign, $this->getAppPrivateKey(), OPENSSL_ALGO_SHA256);
        return  [base64_encode($sign),$nonceStr,$timestamp];
    }

    protected function authorization($body){
        [$signature,$nonceStr,$timestamp] = $this->getSign($body);
        return $this->_arr2str([
            'appid' => "\"{$this->config->get('app_id')}\"",
            'serial_no' => "\"{$this->config->get('serial_no')}\"",
            'timestamp' => "\"{$timestamp}\"",
            'nonce_str' => "\"{$nonceStr}\"",
            'signature' => "\"{$signature}\"",
        ]);;

    }
    /**
     * 新版 数组转字符串
     * @param array $array
     * @return string
     */
    private function _arr2str($array)
    {
        $string = [];
        if ($array && is_array($array)) {
            foreach ($array as $key => $value) {
                $string[] = $key . '=' . $value;
            }
        }
        return join(',', $string);
    }
    /**
     * 获取应用私钥内容
     * @return string
     */
    private function getAppPrivateKey()
    {
        $content = wordwrap($this->trimCert($this->config->get('app_private_key')), 64, "\n", true);
        return "-----BEGIN PRIVATE KEY-----\n{$content}\n-----END PRIVATE KEY-----";
    }

    /**
     * 去除证书前后内容及空白
     * @param string $sign
     * @return string
     */
    protected function trimCert($sign)
    {
        return preg_replace(['/\s+/', '/-{5}.*?-{5}/'], '', $sign);
    }

    /**
     * 以 Post 请求接口
     * @param $url
     * @param $data
     * @param $needSignType
     * @return mixed
     * @throws InvalidResponseException
     * @throws \WeChat\Exceptions\LocalCacheException
     */
    protected function callPostApi($url, $data, $needSignType = true)
    {
        $options['headers'] = [
            "Accept: application/json",
            "Content-Type:application/json"
        ];
        $postData = [ 'req_time' => date('YmdHis'),  'version' => $this->version ,'req_data' => $data ];
        $body = json_encode($postData);
        if ($needSignType)  $options['headers'][] =  "Authorization: " . implode(' ',[$this->schema , $this->authorization($body) ]);
        $result = json_decode(Tools::post($this->gateway . $url ,$body, $options),true);
        if (!isset($result['code']) || $result['code'] !== '000000') {
            throw new InvalidResponseException(
                "Error: " .
                (empty($result['code']) ? '' : $result['message']??$result['msg'] . " [{$result['code']}]\r\n") .
                $result['code'], $result
            );
        }
        return $result['resp_data'];
    }
  
}
